﻿using System;
using System.Windows.Controls;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Automation;
using Tedds_Tool;
using System.Windows;
using GUI.Tabs;
using Tedds_Tool.Instructions;
using System.Windows.Input;
using Tedds_Tool.Automatables;
using StandardScriptItems;
using LEET.LEETCommon;
using CommonInterfaces.Models;
using System.Windows.Media;
using LEET.CommonInterfaces.Models;
using System.Diagnostics;
using System.Collections.Generic;

namespace GUI.Custom_Classes
{
    /// <summary>
    /// Interaction logic for ScriptViewer.xaml
    /// </summary>
    public partial class ScriptViewer : ListBox, IScript
    {
        #region Attributes

        ScriptViewer_AutomationPeer peer;
        public ListBox LineNumbers { get; set; }
        public IInstructionFactory Factory { get; set; }

        /// <summary>
        /// Detects if changes have been made to script to check if user would like to save/overwrite test file.
        /// </summary>
        internal bool scriptchanged;

        /// <summary>
        /// Detects if shift key is pressed and released.
        /// </summary>
        private bool shifting = false;

        /// <summary>
        /// List of existing processes opened by LEET.
        /// </summary>
        public List<Process> processes = new List<Process>();
        public List<string> pronames = new List<string>();

        /// <summary>
        /// Bool value representing test cleanup checkbox in settings advanced options
        /// </summary>
        internal bool TestCleanup = true;

        /// <summary>
        /// Bool value representing screenshot capture feature for test failures in settings advanced options
        /// </summary>
        internal bool TestScreenShot = true;

        #endregion

        public ScriptViewer()
        {
            InitializeComponent();
            peer = new ScriptViewer_AutomationPeer(this);
            KeyDown += new KeyEventHandler(DeselectAll);
            KeyDown += new KeyEventHandler(ShiftSelector);
            KeyUp += new KeyEventHandler(ShiftComplete);
            PreviewMouseDown += new MouseButtonEventHandler(ShiftMouseSelect);
        }

        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
        }

        #region Script_Viewer Shift Selector

        private void ShiftSelector(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
                shifting = true;
        }

        private void ShiftComplete(object sender, KeyEventArgs e)
        {
            shifting = false;
        }

        private void ShiftMouseSelect(object sender, MouseEventArgs e)
        {
            if (!shifting)
            {
                KeyStates lkeys = Keyboard.GetKeyStates(Key.LeftShift);
                KeyStates rkeys = Keyboard.GetKeyStates(Key.RightShift);
                Key skey = Key.None;
                if (lkeys == KeyStates.Down)
                    skey = Key.LeftShift;
                else if (rkeys == KeyStates.Down)
                    skey = Key.RightShift;
                
                ShiftSelector(sender, new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, skey));
            }
            if(shifting)
            {
                int index = indexOfSVClick(e);
                if (index != -1)
                {
                    if (SelectedItems.Count == 0)
                    {
                        for (int i = 0; i < index; i++)
                            SelectedItems.Add(Items.GetItemAt(i));
                    }
                    else
                    {
                        if(index > SelectedIndex)
                            for (int i = SelectedIndex; i < index; i++)
                                SelectedItems.Add(Items.GetItemAt(i));
                        else
                            for (int i = index; i < SelectedIndex; i++)
                                SelectedItems.Add(Items.GetItemAt(i));
                    }
                }
            }
        }

        private int indexOfSVClick(MouseEventArgs e)
        {
            if (Items.Count <= 0)
                return -1;

            // if clicking scroll bar return -1
            Point p = e.GetPosition(this);
            if (p.X > (ActualWidth - 20))
                return -1;
            if (p.Y > (ActualHeight - 20))
                return -1;
            /////////////////////////////////
            int i;
            for (i = 0; i < Items.Count - 1; i++)
            {
                FrameworkElement current = (Items.GetItemAt(i) as FrameworkElement);
                FrameworkElement next = (Items.GetItemAt(i + 1) as FrameworkElement);
                if (current != null)
                {
                    if (e.GetPosition(current).Y >= 0 && e.GetPosition(next).Y < 0)
                        return i;
                }
            }

            FrameworkElement last = (Items.GetItemAt(Items.Count - 1) as FrameworkElement);
            p = e.GetPosition(last);
            if (p.Y >= 0 && p.Y < 23)
            {
                return Items.Count - 1;
            }
            return -1;
        }

        private void DeselectAll(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
                UnselectAll();
        }

        #endregion

        #region Automation

        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            return new ScriptViewer_AutomationPeer(this);
        }

        public class ScriptViewer_AutomationPeer : FrameworkElementAutomationPeer, IValueProvider, ISelectionProvider
        {
            ScriptViewer host;

            public ScriptViewer_AutomationPeer(ScriptViewer rb)
                : base(rb)
            {
                host = rb;
            }

            #region FrameworkElementAutomationPeer

            public override Object GetPattern(PatternInterface patternInterface)
            {
                if (patternInterface.Equals(System.Windows.Automation.Peers.PatternInterface.Value))
                    return this;
                if (patternInterface.Equals(System.Windows.Automation.Peers.PatternInterface.Selection))
                    return this;
                return base.GetPattern(patternInterface);
            }

            protected override string GetClassNameCore()
            {
                return "ScriptViewer";
            }

            protected override string GetAutomationIdCore()
            {
                return "ScriptViewer";
            }

            protected override AutomationControlType GetAutomationControlTypeCore()
            {
                return AutomationControlType.Custom;
            }

            #endregion

            #region IValueProvider Members

            bool IValueProvider.IsReadOnly
            {
                get { return true; }
            }

            void IValueProvider.SetValue(string value)
            {

            }

            string IValueProvider.Value
            {
                get
                {
                    return host.Items.Count.ToString();
                }
            }
            #endregion

            #region ISelectionProvider Members

            bool ISelectionProvider.CanSelectMultiple
            {
                get { return true; }
            }

            IRawElementProviderSimple[] ISelectionProvider.GetSelection()
            {
                IRawElementProviderSimple[] result = new IRawElementProviderSimple[host.SelectedItems.Count];
                int counter = 0;
                foreach (Object o in host.SelectedItems) 
                {
                    if (o is IRawElementProviderSimple) 
                    { 
                        result[counter] = o as IRawElementProviderSimple;
                        ++counter; 
                    }
                }
                return result;
            }

            bool ISelectionProvider.IsSelectionRequired
            {
                get { return false; }
            }

            #endregion
        }
        
        #endregion

        public void AddScriptItem(IEvent_Pair ep)
        {
            scriptchanged = true;
            this.Dispatcher.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
            {
                String lastScriptViewerItem = null;
                if (Count > 0)
                    lastScriptViewerItem = Last;

                if (lastScriptViewerItem != null && isSetVal(ep.Details) && isSetVal(lastScriptViewerItem) && nearMatch(ep.Details, lastScriptViewerItem))
                    RemoveAt(Count - 1);
            
                TextBox temp = new TextBox
                {
                    Name = ("ScriptViewerText" + Items.Count).ToString(),
                    Text = ep.Details,
                    Margin = new Thickness(10, 1, 0, 1)
                };
                temp.TextChanged += new TextChangedEventHandler(delegate(object sender, TextChangedEventArgs args)
                {
                    scriptchanged = true;
                    ToolTipService.SetToolTip(temp, TranslateToPlaintext(temp.Text));
                });
                ToolTipService.SetToolTip(temp, TranslateToPlaintext(temp.Text));
                Items.Add(temp);

                Recalculate_Line_Numbers(Items.Count);
                ScrollToBottom(this);
            }));
        }

        public void AddScriptItem(IScriptBuilder builder)
        {
            scriptchanged = true;
            this.Dispatcher.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
            {
                builder.Name = ("ScriptViewerText" + Items.Count);
                ((ScriptBuilder)builder).MouseLeave += new MouseEventHandler(delegate(object sender, MouseEventArgs args)
                {
                    scriptchanged = true;
                    ToolTipService.SetToolTip((builder as ScriptBuilder), TranslateToPlaintext(builder.ToString()));
                });
                ToolTipService.SetToolTip((builder as ScriptBuilder), TranslateToPlaintext(builder.ToString()));
                Items.Add(builder);

                Recalculate_Line_Numbers(Items.Count);
                ScrollToBottom(this);
            }));
        }

        public static void ScrollToBottom(DependencyObject n)
        {
            // Return the DependencyObject if it is a ScrollViewer
            bool terminate = false;
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(n); i++)
            {
                var child = VisualTreeHelper.GetChild(n, i);
                if (child is ScrollViewer)
                {
                    (child as ScrollViewer).ScrollToBottom();
                    (child as ScrollViewer).LineDown();
                    terminate = true;
                }
            }
            if (!terminate)
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(n); i++)
                    ScrollToBottom(VisualTreeHelper.GetChild(n, i));
        }

        private bool isSetVal(String str)
        {
            //return (Instruction.apply(str) as Tedds_Tool.Instructions.Actions.SetVal) != null;
            return false;
        }

        //nice naming convention (aaa, bbb)...
        private bool nearMatch(String aaa, String bbb)
        {
            int count = 0;
            if ((aaa.Length + 1) != bbb.Length && aaa.Length != (bbb.Length + 1))
                return false;

            CharEnumerator small = aaa.GetEnumerator();
            CharEnumerator big = bbb.GetEnumerator();
            if (aaa.Length > bbb.Length)
            {
                big = aaa.GetEnumerator();
                small = bbb.GetEnumerator();
            }
            else
            {
                small = aaa.GetEnumerator();
                big = bbb.GetEnumerator();
            }

            while (small.MoveNext() && big.MoveNext())
            {
                if (small.Current != big.Current)
                {
                    if (!big.MoveNext() || big.Current != small.Current || count > 0)
                        return false;
                    count++;
                }
            }
            return true;
        }
        
        /// <summary>
        /// Removes all items from the current script.
        /// </summary>
        public void Clear()
        {
            scriptchanged = true;
            this.Dispatcher.Invoke(new System.Windows.Forms.MethodInvoker(delegate()
            {
                Items.Clear();
                Recalculate_Line_Numbers(Items.Count);
            }));
        }

        public System.Collections.Generic.IEnumerator<String> GetEnumerator()
        {
            foreach (Object o in Items)
            {
                yield return this.Dispatcher.Invoke(new StrDelegate(delegate(){
                    object obj = o;
                    if (obj is ComboBoxItem)
                        obj = (obj as ComboBoxItem).Content;
                    if (obj is TextBox)
                        return (obj as TextBox).Text;
                    else
                        return obj.ToString();
                })) as String;
            }
        }

        private delegate String StrDelegate();

        public bool IsEmpty
        {
            get { return Items.IsEmpty; }
        }

        /// <summary>
        /// Translates script line into human readable text comments.
        /// </summary>
        /// <param name="ep">The script line to translate.</param>
        private String TranslateToPlaintext(String ep)
        {
            if (ep == null)
                return null;    //VisualStudio's XML viewer is dumb

            //Trim and seperate data for manageability.
            string retval = null;

            //Retrieve the proper description depending on the values found in the current script function.
            try
            {
                IInstruction instruction = Factory.Interpret(ep);
                retval = instruction.PlainText();
            }
            catch (Exception e)
            {
                retval = null;
            }
            if (string.IsNullOrWhiteSpace(retval))
            {
                retval = "Incomplete script line, check all arguments...";
            }

            return retval;
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Removes script line at the specified index.
        /// </summary>
        /// <param name="index">The index of the line to remove.</param>
        public void RemoveAt(int index)
        {
            Items.RemoveAt(index);
            Recalculate_Line_Numbers(Items.Count);
        }

        public int Count
        {
            get { return Items.Count; }
        }

        public string Last
        {
            get
            { 
                Object obj = Items[Count -1];
                if (obj is TextBox)
                    return (obj as TextBox).Text;
                return obj.ToString(); 
            }
        }

        /// <summary>
        /// Removes all the currently selected script lines.
        /// </summary>
        public void RemoveSelected()
        {
            scriptchanged = true;
            while (SelectedItems.Count > 0)
            {
                if (SelectedIndex >= 0 && SelectedIndex < Count)
                {
                    int index = SelectedIndex;
                    RemoveAt(index);
                }
            }
            Recalculate_Line_Numbers(Items.Count);
        }

        internal void TestFinished()
        {
            if (TestCleanup == true)
                KillAll();
        }

        internal void PaintError(int index)
        {
            index -= 1;
            (Items.GetItemAt(index) as Control).Background = Brushes.Red;
        }

        internal void UnPaintErrors()
        {
            for (int index = 0; index < Items.Count; index++)
            {
                (Items.GetItemAt(index) as Control).Background = Brushes.White;
            }
        }

        /// <summary>
        /// Terminates processes being watched by LEET for test cleanup feature in advanced options.
        /// </summary>
        private void KillAll()
        {
            foreach (Process pro in processes)
            {
                if (!pro.HasExited)
                {
                    try { pro.CloseMainWindow(); }
                    catch { }
                    try { pro.Kill(); }
                    catch { }
                }
            }
        }

        /// <summary>
        /// Redoes the line number column to the left of the ScriptViewer on the CRT tab.
        /// </summary>
        public void Recalculate_Line_Numbers(int NumberOfLines)
        {
            if (NumberOfLines == 0)
            {
                LineNumbers.Items.Clear();
                return;
            }
            if (LineNumbers.Items.Count == NumberOfLines)
                return;

            while (LineNumbers.Items.Count < NumberOfLines)
            {
                Label temp = new Label
                {
                    Name = "ScriptLineNumber" + LineNumbers.Items.Count,
                    Content = (LineNumbers.Items.Count + 1).ToString(),
                    Height = 24
                };
                LineNumbers.Items.Add(temp);
            }
            while (LineNumbers.Items.Count > NumberOfLines)
            {
                LineNumbers.Items.RemoveAt(LineNumbers.Items.Count - 1);
            }

            //What's this for?
            //Border border = new Border { Height = 24 };
            //LineNumbers.Items.Add(border);
        }
    }
}
